<?php

/*
Plugin Name: Text Link Ads InLinks
Plugin URI: http://www.text-link-ads.com/
Description: Text Link Ads InLinks program sells links within your blog posts
Author: Text Link Ads
Version: 1.2.6
Author URI: http://www.text-link-ads.com/
*/

if(!function_exists('add_action')){
    header('HTTP/1.0 404 Not Found');
    header('Location: ../../');
    exit();
}

//flushes the supercache that is used for new WP installs
if (function_exists('wp_cache_flush')) {
    wp_cache_flush();
    
}
$wp_cache_shutdown_gc = 1;
//

function tla_disable_plugin($inlinks = false)
{
    $pluginName = 'tla_'.($inlinks ? 'inlinks' : '310705').'.php';
    $plugins = get_option('active_plugins');
    $index = array_search( $pluginName, $plugins);
    if($index !== false){
        array_splice($plugins, $index, 1 );
        update_option('active_plugins', $plugins);
        do_action('deactivate_'.$pluginName);
    }
}


if(!function_exists('mysql_real_escape_string')){
    echo('You must be running PHP 4.3.0 or higher to use the Text Link Ads InLinks plugin. Please contact your web host about upgrading.');
    tla_disable_plugin(true);
    exit;
}


if(function_exists('tla_ads')){

    tla_disable_plugin();

}else{

$textlinkads_object = null;

// general/syncing hooks
add_action('init',            'tla_initialize');
add_action('publish_post',    'tla_send_new_post_alert');
add_action('publish_page',    'tla_send_new_post_alert');
add_action('delete_post',     'tla_send_deleted_post_alert');
add_action('activate_tla_inlinks.php', 'tla_check_installation');


function tla_ads() {}


function tla_initialize()
{
    global $wpdb, $textlinkads_object;
    $textlinkads_object = new textlinkadsObject;
    $textlinkads_object->initialize();

    tla_disable_plugin();
    
    if($_REQUEST['textlinkads_key'] == $textlinkads_object->websiteKey){

        switch($_REQUEST['textlinkads_action'])
        {
    
            case 'debug':
                $textlinkads_object->debug();
                exit;
    
            case 'search_posts':
                if($_REQUEST['textlinkads_key'] == $textlinkads_object->websiteKey){
                    $textlinkads_object->searchPosts($_REQUEST['textlinkads_query']);
                }
                exit;
    
            case 'sync_posts':
                if(isset($_REQUEST['textlinkads_post_id']) && !empty($_REQUEST['textlinkads_post_id']))
                    $textlinkads_object->outputPostForSyncing($_REQUEST['textlinkads_post_id']);
                else
                    $textlinkads_object->initialPostSync();
                exit;
    
            case 'reset_syncing':
                update_option($textlinkads_object->lastSyncIdOption, '0');
                break;
    
            case 'reset_sync_limit':
                $maxId = $wpdb->get_var("SELECT ID FROM $wpdb->posts ORDER BY ID DESC LIMIT 1");
                if($maxId === '') $maxId = '0';
                update_option($textlinkads_object->maxSyncIdOption, $maxId);
                break;
        }
        
    }

    if(!is_feed()) add_filter('the_content', 'tla_insert_inlink', 1);
}


function tla_check_installation()
{
    global $textlinkads_object;

    $textlinkads_object = new TextLinkAdsObject;
    $textlinkads_object->checkInstallation();
}


function tla_insert_inlink($content = '')
{
    global $textlinkads_object, $wpdb, $post;

    $textlinkads_object = new textlinkadsObject;
    $textlinkads_object->initialize();

    if(is_object($post)) $content = $textlinkads_object->insertInLinkAd($post->ID, $content);

    return $content;
}


function tla_send_new_post_alert($postId)
{
    global $textlinkads_object;

    $textlinkads_object->postLevelPing($textlinkads_object->PingUrl.'?action=add&inventory_key='.$textlinkads_object->websiteKey.'&post_id='.$postId);
}


function tla_send_updated_post_alert($postId)
{
    global $textlinkads_object;

    $textlinkads_object->postLevelPing($textlinkads_object->PingUrl.'?action=update&inventory_key='.$textlinkads_object->websiteKey.'&post_id='.$postId);
}


function tla_send_deleted_post_alert($postId)
{
    global $textlinkads_object;

    $textlinkads_object->postLevelPing($textlinkads_object->PingUrl.'?action=delete&inventory_key='.$textlinkads_object->websiteKey.'&post_id='.$postId);
}



class textlinkadsObject
{
    var $websiteKey         = 'UXRMI17XUCB94YW91NFT';

    // we do not recommend changing these values
    var $BaseUrl        = 'http://www.text-link-ads.com/';
    var $PingUrl         = 'http://www.text-link-ads.com/post_level_sync.php';
    var $xmlRefreshTime     = 900;
    var $connectionTimeout  = 10;
    var $version    ='1.2.6';
    var $DataTable       = 'tla_data';
    var $rssMapTable        = 'tla_rss_map';

    var $lastUpdateOption   = 'tla_last_update';
    var $lastSyncIdOption   = 'tla_last_sync_post_id';
    var $maxSyncIdOption    = 'tla_max_sync_post_id';

    var $ads;


    function __construct()
    {
        global $table_prefix;

        $this->DataTable = $table_prefix.$this->DataTable;
        $this->rssMapTable  = $table_prefix.$this->rssMapTable;
    }

    function debug()
    {
        global $wpdb;

        echo '<html>';
        echo '<head>';
        echo '<meta name="ROBOTS" content="NOINDEX,NOFOLLOW">';
        echo '</head>';
        echo '<body>';
        echo "<b>Last Refresh:</b> " .get_option($this->lastUpdateOption) ."<br><br>\n";
        echo "Version : " . $this->version . " <br><br>\n";
        
        if($wpdb->get_var("SHOW TABLES LIKE '$this->DataTable'") != $this->DataTable) {
            echo "Text Link Ads data table is <b>not installed</b> (".$this->DataTable.")<br><br>\n";
        }else{
            echo "Text Link Ads data table is installed (".$this->DataTable.")<br><br>\n";
            print_r($wpdb->get_results("SELECT * FROM `$this->DataTable`"));
            echo "<br><br>";
        }
        
        echo '</body>';
        echo '</html>';
    }

    function installDatabase()
    {
        global $wpdb;

        require_once(ABSPATH . 'wp-admin/upgrade-functions.php');

        $sql = "CREATE TABLE `$this->DataTable` (
                  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
                  `post_id` bigint(20) unsigned NOT NULL default '0',
                  `url` TEXT NOT NULL,
                  `text` TEXT NOT NULL,
                  `before_text` TEXT NOT NULL,
                  `after_text` TEXT NOT NULL,
                  `rss_text` TEXT NOT NULL,
                  `rss_before_text` TEXT NOT NULL,
                  `rss_after_text` TEXT NOT NULL,
                  `rss_prefix` VARCHAR(255) NOT NULL DEFAULT '',
                  PRIMARY KEY  (`id`),
                  KEY `post_id` (`post_id`)
                ) TYPE=MyISAM AUTO_INCREMENT=1;";

        dbDelta($sql);
    
        $sql = "ALTER TABLE `$this->DataTable` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
        @dbDelta($sql);
        
        $sql = "CREATE TABLE `$this->rssMapTable` (
                    `post_id` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0',
                    `advertisement` TEXT NOT NULL ,
                    PRIMARY KEY ( `post_id` )
                ) TYPE = MYISAM;";

        dbDelta($sql);

        $sql = "ALTER TABLE `$this->rssMapTable` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
        @dbDelta($sql);
        
        
        add_option($this->rssInstalledOption, date('Y-m-d H:i:s'), 'Stores the date that Text Link Ads rss was installed.');
        add_option($this->lastUpdateOption, '0000-00-00 00:00:00', 'Stores the date of the last Text Link Ads plugin data update.');
        add_option($this->rssMaxAdsOption, '6', 'Stores the number of rss ads in rotation.');
        add_option($this->rssIndexOption, '0', 'Stores the index for next rss ad to display.');
        
        if( get_option($this->maxSyncIdOption) > 0 ) return;

        $maxId = $wpdb->get_var("SELECT ID FROM $wpdb->posts ORDER BY ID DESC LIMIT 1");
        if($maxId === '') $maxId = '0';

        add_option($this->lastSyncIdOption, '0', 'The ID of the last post synced with Text Link Ads');
        add_option($this->maxSyncIdOption, $maxId, 'The highest post ID to be batch synced with Text Link Ads');

        $this->postLevelPing($this->PingUrl.'?action=install&inlinks=true&inventory_key='.$this->websiteKey.'&site_url='.urlencode(get_option('siteurl')), 80);
    }


    function installPostLevel()
    {
        global $wpdb;

        require_once(ABSPATH . 'wp-admin/upgrade-functions.php');

        $wpdb->query("ALTER TABLE `$this->DataTable` ADD `post_id` BIGINT( 20 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `id` ;");

        $wpdb->query("ALTER TABLE `$this->DataTable` ADD INDEX ( `post_id` ) ;");
        
        if( get_option($this->maxSyncIdOption) > 0 ) return;

        $maxId = $wpdb->get_var("SELECT ID FROM $wpdb->posts ORDER BY ID DESC LIMIT 1");
        if($maxId === '') $maxId = '0';

        add_option($this->lastSyncIdOption, '0', 'The ID of the last post synced with Text Link Ads');
        add_option($this->maxSyncIdOption, $maxId, 'The highest post ID to be batch synced with Text Link Ads');

        $this->postLevelPing($this->PingUrl.'?action=install&inlinks=true&inventory_key='.$this->websiteKey.'&site_url='.urlencode(get_option('siteurl')));
    }


    function checkInstallation()
    {
        global $wpdb;

        if($wpdb->get_var("SHOW TABLES LIKE '$this->DataTable'") != $this->DataTable) {
            $this->installDatabase();
        }else if($wpdb->get_var("SHOW COLUMNS FROM $this->DataTable LIKE 'post_id'") != 'post_id'){
            $this->installPostLevel();
        }
    }


    function initialize()
    {
        global $wpdb;
        
        $this->checkInstallation();

        if( get_option($this->lastUpdateOption) < date('Y-m-d H:i:s', time() - $this->xmlRefreshTime) ||
            get_option($this->lastUpdateOption) > date('Y-m-d H:i:s') )
        {
            $this->updateLocalAds($this->BaseUrl."xml.php?inlinks=true&inventory_key=".$this->websiteKey.'&site_url='.urlencode(get_option('siteurl')));
        }

        $this->ads = array();

        $ads = $wpdb->get_results("SELECT * FROM $this->DataTable WHERE post_id > 0");

        if(!is_array($ads)) return;

        foreach($ads as $ad){
            if(is_array($this->ads[$ad->post_id])){
                $this->ads[$ad->post_id][] = $ad;
            }else{
                $this->ads[$ad->post_id] = array($ad);
            }
        }
    }


    function updateLocalAds($url)
    {
        global $wpdb;

        update_option($this->lastUpdateOption, date('Y-m-d H:i:s'));

        if($xml = $this->fetchLiveXml($url)) {

            $xmlData = $this->decodeXml($xml);

            $wpdb->query("TRUNCATE `$this->DataTable`");

            if( is_array($xmlData['URL']) ){

                $query = "INSERT INTO $this->DataTable ( `url`, `post_id`, `text`) VALUES ";
                for ($i = 0; $i < count($xmlData['URL']); $i++) {
                    $query .= " (
                        '".mysql_real_escape_string($xmlData['URL'][$i])."',
                        '".mysql_real_escape_string($xmlData['PostID'][$i])."',
                        '".mysql_real_escape_string(trim($xmlData['Text'][$i]))."'
                    ),";

                }

                $query = substr($query, 0, strlen($query)-1);
                $wpdb->query($query);
            }
        }
    }


    function postLevelPing($url)
    {
        $url = parse_url($url);

        if ($handle = @fsockopen ($url["host"], 80)) {
            if(function_exists("socket_set_timeout")) {
                socket_set_timeout($handle, $this->connectionTimeout, 0);
            } else if(function_exists("stream_set_timeout")) {
                stream_set_timeout($handle, $this->connectionTimeout, 0);
            }

            fwrite ($handle, "GET $url[path]?$url[query] HTTP/1.0\r\nHost: $url[host]\r\nConnection: Close\r\n\r\n");
            fclose($handle);

            return true;
        }

        return false;
    }


    function fetchLiveXml($url)
    {
        $result = '';
        $url = parse_url($url);

        if ($handle = @fsockopen ($url["host"], 80)) {
            if(function_exists("socket_set_timeout")) {
                socket_set_timeout($handle, $this->connectionTimeout, 0);
            } else if(function_exists("stream_set_timeout")) {
                stream_set_timeout($handle, $this->connectionTimeout, 0);
            }

            fwrite ($handle, "GET $url[path]?$url[query] HTTP/1.0\r\nHost: $url[host]\r\nConnection: Close\r\n\r\n");
            while (!feof($handle)) {
                $result .= @fread($handle, 40960);
            }
            fclose($handle);

            $result = substr($result, strpos($result,'<?'));
        }

        return $result;
    }


    function decodeXml($xml)
    {

        if( !function_exists('html_entity_decode') ){
            function html_entity_decode($string)
            {
               // replace numeric entities
               $str = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\1"))', $str);
               $str = preg_replace('~&#([0-9]+);~e', 'chr(\1)', $str);
               // replace literal entities
               $transTable = get_html_translation_table(HTML_ENTITIES);
               $transTable = array_flip($transTable);
               return strtr($str, $transTable);
            }
        }

        $out        = array();
        $returnData = array();

        preg_match_all ("/<(.*?)>(.*?)</", $xml, $out, PREG_SET_ORDER);
        $search  = array('&#60;', '&#62;', '&#34;');
        $replace = array('<', '>', '"');
        $n = 0;
        while (isset($out[$n]))
        {
            $returnData[$out[$n][1]][] = str_replace($search, $replace, html_entity_decode(strip_tags($out[$n][0])));
            $n++;
        }
        return $returnData;
    }


    function insertInLinkAd($postId, $content)
    {
        if(is_array($this->ads[$postId])){

            foreach($this->ads[$postId] as $ad){

                $specialChars = array('/', '*', '+', '?', '^', '$', '[', ']', '(', ')');
                $specialCharsEsc = array('\/', '\*', '\+', '\?', '\^', '\$', '\[', '\]', '\(', '\)');
                
                $specialMassage='(\')?(s)?(-)?';                
                $escapedLinkText = str_replace($specialChars, $specialCharsEsc, $ad->text);
                if (strpos($escapedLinkText,' ')!==false){
                    $LinkTexts=explode(' ',$escapedLinkText);
                    $escapedLinkText='';
                    foreach ($LinkTexts as $L){
                        if ($second){
                            $escapedLinkText.=' ';
                        }
                        if (substr($L,-1)=='s'){
                            $L=substr($L,0,-1);
                        }                            
                        $second=true;
                        $escapedLinkText.=$L.$specialMassage;
                        if ($L!=end($LinkTexts)){
                            $escapedLinkText.='(\s)?';
                        }
                    }
                } else {
                    if (substr($escapedLinkText,-1)=='s'){
                        $escapedLinkText=substr($escapedLinkText,0,-1);
                    }
                    $escapedLinkText.=$specialMassage;
                    
                }
                $find = '/\b'.$escapedLinkText.'\b/i';
                $trueMatch = false;

                $matches = array();
                preg_match_all($find, $content, $matches, PREG_OFFSET_CAPTURE);
                $matchData = $matches[0];

                if(count($matchData) > 1){

                    $invalidMatches = array(
                        '/<h[1-6][^>]*>[^<]*'.$escapedLinkText.'[^<]*<\/h[1-6]>/i',
                        '/<a[^>]+>[^<]*'.$escapedLinkText.'[^<]*<\/a>/i',
                        '/href=("|\')[^"\']+'.$escapedLinkText.'[^"\']+("|\')/i',
                        '/src=("|\')[^"\']*'.$escapedLinkText.'[^"\']*("|\')/i',
                        '/alt=("|\')[^"\']*'.$escapedLinkText.'[^"\']*("|\')/i',
                        '/title=("|\')[^"\']*'.$escapedLinkText.'[^"\']*("|\')/i',
                        '/content=("|\')[^"\']*'.$escapedLinkText.'[^"\']*("|\')/i',
                        '/<script[^>]*>[^<]*'.$escapedLinkText.'[^<]*<\/script>/i'
                    );

                    foreach($invalidMatches as $invalidMatch){
                        $this->flagInvalidMatch($matchData, $invalidMatch, $content);
                    }

                    foreach($matchData as $index => $match){
                        if($match[2] != true){
                            $trueMatch = $match;
                            break;
                        }
                    }
                }else{
                    $trueMatch = $matchData[0];
                }

                if(is_array($trueMatch)){
                    $replacement = '<a href="'.$ad->url.'">'.$trueMatch[0].'</a>';
                    $content = substr($content, 0, $trueMatch[1]) . $replacement . substr($content, $trueMatch[1] + strlen($trueMatch[0]));
                }

            }

        }

        return $content;
    }


    function flagInvalidMatch(&$matchData, $pattern, $content)
    {
        $results = array();
        preg_match_all($pattern, $content, $results, PREG_OFFSET_CAPTURE);
        $matches = $results[0];

        if(count($matches) == 0) return;

        foreach($matches as $match){
            $offsetMin = $match[1];
            $offsetMax = $match[1] + strlen($match[0]);
            foreach($matchData as $index => $data){
                if($data[1] >= $offsetMin && $data[1] <= $offsetMax){
                    $matchData[$index][2] = true;
                }
            }
        }

    }


    function searchPosts($query)
    {
        global $wpdb;

        $sql = "SELECT ID FROM $wpdb->posts
                WHERE (post_status = 'publish' OR post_status = 'static') AND post_content LIKE '%$query%'";

        if($query != '') $posts = $wpdb->get_results($sql);

        echo "<?xml version=\"1.0\" ?>\n<posts>\n";
        if(is_array($posts)){
            $lastIndex = count($posts) - 1;
            foreach($posts as $index => $post){
                echo $post->ID.($index != $lastIndex ? ',' : '');
            }
        }
        echo "</posts>\n";
        exit();
    }


    function outputPostForSyncing($postId)
    {
        global $wpdb;

        $posts = $wpdb->get_results("SELECT ID, post_date_gmt, post_content, post_title FROM $wpdb->posts WHERE ID = '$postId'");

        $this->outputPostsForSyncing($posts);
    }


    function outputPostsForSyncing($posts)
    {
        header('Content-type: application/xml');
        echo "<?xml version=\"1.0\" ?>\n<posts>\n";
        if(is_array($posts)){

            foreach($posts as $post){
                echo "<post>\n"
                        . "<id>".$post->ID."</id>\n"
                        . "<title>".urlencode($post->post_title)."</title>\n"
                        . "<date>".$post->post_date_gmt."</date>\n"
                        . "<url>".get_permalink($post->ID)."</url>\n"
                        . "<body>".$this->prepareBody($post->post_content)."</body>\n"
                    . "</post>\n";
            }

        }
        echo "</posts>\n";
        exit();
    }


    function prepareBody($body)
    {
        $search = array (   '@<script[^>]*?>.*?</script>@si',
                            '@<h[1-6][^>]*?>.*?</h[1-6]>@si',
                            '@<a[^>]*?>.*?</a>@si',
                            '@<[\/\!]*?[^<>]*?>@si',
                            '@&[#a-z0-9]+;@si',
                            '@"@',
                            "@'@",
                            '@>@',
                            '@<@',
                            '@[\r\n\t\s]+@');
        $replace = array ('', '', '', '', '', '', '', '', '', ' ');

        return urlencode( trim( preg_replace($search, $replace, $body) ) );
    }


    function initialPostSync()
    {
        global $wpdb;

        $lastId = get_option($this->lastSyncIdOption);
        $maxId = get_option($this->maxSyncIdOption);

        if($lastId === '' || $lastId === false) $lastId = 0;
        if($maxId === '' || $maxId === false)  $maxId  = 999999;

        $query   = "SELECT ID, post_date_gmt, post_content, post_title FROM $wpdb->posts
                    WHERE (post_status = 'publish' OR post_status = 'static') AND ID > '$lastId' AND ID <= '$maxId'
                    ORDER BY ID ASC LIMIT 100";


        $posts = $wpdb->get_results($query);

        if(is_array($posts) && count($posts) > 0){
            $lastIndex = count($posts) - 1;
            $lastId = $posts[$lastIndex]->ID;
            update_option($this->lastSyncIdOption, $lastId);
        }

        $this->outputPostsForSyncing($posts);
    }


}

}
?>